use {
    crate::base::{
        client::Client,
        params::{BroadcasthexParams, TriggerSmartContractParams, TriggerconstantcontractParams},
        response::{
            transaction::TxId, trigger_constant_contract::TriggerConstantContractResult,
            trigger_smart_contract::TriggerSmartContractResult,
        },
    },
    anychain_core::Transaction,
    anychain_tron::TronAddress,
    alloy::primitives::U256,
    std::str::FromStr,
    tracing::info,
};

pub async fn token_balance(
    client: &Client,
    contract_address: &str,
    owner_address: &str,
) -> anyhow::Result<String> {
    let owner_param = TronAddress::from_str(owner_address).unwrap().to_hex();
    let parameter = format!("{:0>width$}", owner_param, width = 64);
    let ret: TriggerConstantContractResult = client
        .post(
            "wallet/triggerconstantcontract",
            TriggerconstantcontractParams::new(
                contract_address.to_string(),
                "balanceOf(address)".to_string(),
                owner_address.to_string(),
                parameter,
            ),
        )
        .await?;

    let bal_ret = ret.constant_result.get(0).unwrap();
    let bal_radix_str = bal_ret.trim_start_matches('0');
    let bal = U256::from_str_radix(bal_radix_str, 16).unwrap();

    Ok(bal.to_string())
}

pub async fn allowance_token(
    client: &Client,
    contract_address: &str,
    owner_address: &str,
    spender_address: &str,
) -> anyhow::Result<U256> {
    let owner_hex = TronAddress::from_str(owner_address).unwrap().to_hex();
    let owner_param = format!("{:0>width$}", owner_hex, width = 64);
    let spender_hex = TronAddress::from_str(spender_address).unwrap().to_hex();
    let spender_param = format!("{:0>width$}", spender_hex, width = 64);
    let parameter = format!("{}{}", owner_param, spender_param);
    let ret: TriggerConstantContractResult = client
        .post(
            "wallet/triggerconstantcontract",
            TriggerconstantcontractParams::new(
                contract_address.to_string(),
                "allowance(address,address)".to_string(),
                owner_address.to_string(),
                parameter,
            ),
        )
        .await?;

    let allowance_ret = ret.constant_result.get(0).unwrap();
    let allowance_radix_str = allowance_ret.trim_start_matches('0');
    let allowance = U256::from_str_radix(allowance_radix_str, 16).unwrap();

    Ok(allowance)
}

const APPROVE_LIMIT: &str = "100_000_000_000_000_000_000";
const FEE_LIMIT: u32 = 100_000_000;
pub async fn approve_token(
    client: &Client,
    contract_str: &str,
    principal_key_str: &str,
    principal_pk_str: &str,
    agent_pk_str: &str,
    number: u128,
) -> anyhow::Result<String> {
    let principal_key_bytes = hex::decode(principal_key_str).unwrap();
    let principal = libsecp256k1::SecretKey::parse_slice(&principal_key_bytes).unwrap();
    let agent_hex = TronAddress::from_str(agent_pk_str).unwrap().to_hex();
    let agent_param = format!("{:0>width$}", agent_hex, width = 64);
    let amount_param = format!("{:0>64x}", number);
    let parameter = format!("{}{}", agent_param, amount_param);
    let ret: TriggerSmartContractResult = client
        .post(
            "wallet/triggersmartcontract",
            TriggerSmartContractParams::new(
                contract_str.to_string(),
                "approve(address,uint256)".to_string(),
                principal_pk_str.to_string(),
                parameter,
                FEE_LIMIT,
                0,
            ),
        )
        .await?;

    let tx_raw_hex = ret.transaction.raw_data_hex;
    let tx_raw_data = hex::decode(tx_raw_hex).unwrap();

    let mut tron_tx =
        anychain_tron::transaction::TronTransaction::from_bytes(&tx_raw_data).unwrap();

    let hash = anychain_core::crypto::sha256(&tx_raw_data);
    let message = libsecp256k1::Message::parse(&hash);
    let (signature, rec_id) = libsecp256k1::sign(&message, &principal);
    let tx_bytes = tron_tx
        .sign(signature.serialize().to_vec(), rec_id.serialize())
        .unwrap();
    let tx_hex = hex::encode(tx_bytes);

    let ret: TxId = client
        .post("wallet/broadcasthex", BroadcasthexParams::new(tx_hex))
        .await?;

    Ok(ret.tx_id)
}

pub async fn transfer_token(
    client: &Client,
    contract: &str,
    payer: &str,
    from: &str,
    to: &str,
    amount: u128,
) -> anyhow::Result<String> {
    let to_str = TronAddress::from_str(to)?.to_hex();
    let to_param = format!("{:0>width$}", to_str, width = 64);
    let amount_param = format!("{:0>64x}", amount);
    let parameter = format!("{}{}", to_param, amount_param);
    let ret: TriggerSmartContractResult = client
        .post(
            "wallet/triggersmartcontract",
            TriggerSmartContractParams::new(
                contract.to_string(),
                "transfer(address,uint256)".to_string(),
                from.to_string(),
                parameter,
                FEE_LIMIT,
                0,
            ),
        )
        .await?;

    let tx_raw_hex = ret.transaction.raw_data_hex;
    let tx_raw_data = hex::decode(tx_raw_hex).unwrap();

    let payer_bytes = hex::decode(payer).unwrap();

    let mut tron_tx =
        anychain_tron::transaction::TronTransaction::from_bytes(&tx_raw_data).unwrap();

    let hash = anychain_core::crypto::sha256(&tx_raw_data);
    let message = libsecp256k1::Message::parse(&hash);
    let secret_key = libsecp256k1::SecretKey::parse_slice(&payer_bytes).unwrap();
    let (signature, rec_id) = libsecp256k1::sign(&message, &secret_key);
    let tx_bytes = tron_tx
        .sign(signature.serialize().to_vec(), rec_id.serialize())
        .unwrap();
    let tx_hex = hex::encode(tx_bytes);

    let ret: TxId = client
        .post("wallet/broadcasthex", BroadcasthexParams::new(tx_hex))
        .await?;

    Ok(ret.tx_id)
}

pub async fn transfer_from_token(
    client: &Client,
    contract_str: &str,
    payer_key_str: &str,
    payer_str: &str,
    from_str: &str,
    to_str: &str,
    number: u128,
) -> anyhow::Result<String> {
    let payer_bytes = hex::decode(payer_key_str).unwrap();
    let secret_key = libsecp256k1::SecretKey::parse_slice(&payer_bytes).unwrap();
    let from_hex = TronAddress::from_str(from_str)?.to_hex();
    let from_param = format!("{:0>width$}", from_hex, width = 64);
    let to_hex = TronAddress::from_str(to_str)?.to_hex();
    let to_param = format!("{:0>width$}", to_hex, width = 64);
    let amount_param = format!("{:0>64x}", number);
    let parameter = format!("{}{}{}", from_param, to_param, amount_param);
    let ret: TriggerSmartContractResult = client
        .post(
            "wallet/triggersmartcontract",
            TriggerSmartContractParams::new(
                contract_str.to_string(),
                "transferFrom(address,address,uint256)".to_string(),
                payer_str.to_string(),
                parameter,
                FEE_LIMIT,
                0,
            ),
        )
        .await?;

    let tx_raw_hex = ret.transaction.raw_data_hex;
    let tx_raw_data = hex::decode(tx_raw_hex).unwrap();

    let mut tron_tx =
        anychain_tron::transaction::TronTransaction::from_bytes(&tx_raw_data).unwrap();

    let hash = anychain_core::crypto::sha256(&tx_raw_data);
    let message = libsecp256k1::Message::parse(&hash);
    let (signature, rec_id) = libsecp256k1::sign(&message, &secret_key);
    let tx_bytes = tron_tx
        .sign(signature.serialize().to_vec(), rec_id.serialize())
        .unwrap();
    let tx_hex = hex::encode(tx_bytes);

    let ret: TxId = client
        .post("wallet/broadcasthex", BroadcasthexParams::new(tx_hex))
        .await?;

    Ok(ret.tx_id)
}
